/*******************************************************************************
 File Name        : freyabaseaction.cpp
 Summary          : Base class of synchronous message processing
 Create           : 2015-04-27
 Author           : Schindleren
 Update history   : (Look up at freyabaseaction.h)
*******************************************************************************/
#include "freyabaseaction.h"

FreyaActionDataQ::FreyaActionDataQ(FreyaBaseAction *pActObj, const char *SubsysName, bool bIsAsync, int cachesize) :
    QThread(), m_pActObj(pActObj), m_bIsAsync(bIsAsync), m_ExecEventLoop(nullptr), m_DataRing(cachesize)
{
    QString strThreadName = "ActDQ";
    if(SubsysName)
    {
        strThreadName = QString::fromUtf8(SubsysName) + "_" + strThreadName;
        setObjectName(strThreadName);
    }

    start();
}

FreyaActionDataQ::~FreyaActionDataQ()
{
    terminate();
    m_ExecEventLoop->deleteLater();
}

void FreyaActionDataQ::run()
{
    m_ExecEventLoop = new QEventLoop();

    while (true)
    {
        FreyaData pData;
        if(!PopData(pData))
        {
            m_ExecEventLoop->exec();
            continue;
        }

        // 实际的消息执行接口
        if(m_bIsAsync)
        {
            // （相较于FreyaBaseAction对象）异步方法
            OnExecute(pData);
        }
        else
        {
            // （相较于FreyaBaseAction对象）同步方法
            QMetaObject::invokeMethod(this, "OnExecute", Qt::QueuedConnection, Q_ARG(FreyaData, pData));
        }
    }
}

bool FreyaActionDataQ::PopData(FreyaData &pData)
{
    if(m_DataRing.pull(pData))
    {
        return true;
    }
    else if(!m_DataQueue.isEmpty())
    {
        QMutexLocker locker(&m_QueueMutex);
        pData = m_DataQueue.takeLast();
        return true;
    }
    else
    {
        return false;
    }
}

void FreyaActionDataQ::OnExecute(FreyaData pData)
{
    m_pActObj->Execute(pData);
}

void FreyaActionDataQ::ExecuteCache(const FreyaData pData)
{
    if(!m_DataQueue.isEmpty() && m_DataRing.isEmpty())
    {
        while(!m_DataQueue.isEmpty())
        {
            QMutexLocker locker(&m_QueueMutex);
            if(!m_DataRing.push(m_DataQueue.takeFirst()))
            {
                break;
            }
        }
    }

    if(!m_DataQueue.isEmpty() || !m_DataRing.push(pData))
    {
        QMutexLocker locker(&m_QueueMutex);
        m_DataQueue.append(pData);
    }

    if(m_ExecEventLoop->isRunning())
    {
        m_ExecEventLoop->quit();
    }
}

/////////////////////////////////////////////

FreyaBaseAction::FreyaBaseAction(FreyaBaseControl *pControl, const char *actionName, bool bIsAsync, int cachesize) :
    FreyaAbstractAction(), m_FreyaBaseControl(pControl), m_CommandList(QList<quint64>()),
    m_pFreyaActionDataQ(new FreyaActionDataQ(this, actionName, bIsAsync, cachesize))
{
    qInfo() << "FreyaLib > " << "FreyaBaseAction Object Structured:" << actionName << "with:" << (bIsAsync ? "ASYNC" : "SYNC");

    // Register this action to control
    if(actionName)
    {
        if(m_FreyaBaseControl->RegisterObject(this, actionName))
        {
            m_ActionName = actionName;
        }
    }
}

FreyaBaseAction::~FreyaBaseAction()
{
    m_FreyaBaseControl->UnRegisterObject(this);

    qInfo() << "FreyaLib > " << m_ActionName << this << "terminate";
    m_pFreyaActionDataQ->deleteLater();
}

QString FreyaBaseAction::ActionName()
{
    return m_ActionName;
}

void FreyaBaseAction::ExecuteCache(const FreyaData pData)
{
    m_pFreyaActionDataQ->ExecuteCache(pData);
}

void FreyaBaseAction::RegisterCommands()
{
    // 消息注册指令
    FreyaData pRegisterData = FreyaBaseData::CreateData(FREYALIB_CMD_CMDREGREQUEST);
    Execute(pRegisterData);
    m_FreyaBaseControl->RegisterCommand(this, m_CommandList);
}

